import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, DataSource } from 'typeorm';
import { DeviceAuditCode } from './entities/device-audit-code.entity';
import { AuditStatus } from '../common/enums';

@Injectable()
export class DeviceAuditCodeRepository {
  constructor(
    @InjectRepository(DeviceAuditCode)
    private readonly repository: Repository<DeviceAuditCode>,
    private readonly dataSource: DataSource,
  ) {}

  /**
   * Find an audit code by audit number and verify code
   */
  async findByAuditNumberAndVerifyCode(
    auditNumber: string,
    verifyCode: string,
  ): Promise<DeviceAuditCode | null> {
    return this.repository.findOne({
      where: {
        auditNumber,
        verifyCode,
        isDeleted: false,
      },
    });
  }

  /**
   * Find an audit code by device serial number
   */
  async findByDeviceSerialNumber(deviceSerialNumber: string): Promise<DeviceAuditCode | null> {
    return this.repository.findOne({
      where: {
        deviceSerialNumber,
        isDeleted: false,
      },
    });
  }

  /**
   * Create a new audit code
   */
  async create(auditCode: Partial<DeviceAuditCode>): Promise<DeviceAuditCode> {
    const newAuditCode = this.repository.create(auditCode);
    return this.repository.save(newAuditCode);
  }

  /**
   * Create multiple audit codes in batch within a transaction
   * @param auditCodes Array of audit codes to create
   * @returns Array of created audit codes
   */
  async createBatchInTransaction(
    auditCodes: Partial<DeviceAuditCode>[],
  ): Promise<DeviceAuditCode[]> {
    // Use a transaction to ensure all codes are created or none
    return this.dataSource.transaction(async (transactionalEntityManager) => {
      const newAuditCodes = transactionalEntityManager.create(DeviceAuditCode, auditCodes);
      return transactionalEntityManager.save(newAuditCodes);
    });
  }

  /**
   * Update an existing audit code
   */
  async update(auditNumber: string, auditCode: Partial<DeviceAuditCode>): Promise<DeviceAuditCode> {
    await this.repository.update({ auditNumber }, auditCode);
    return this.repository.findOne({ where: { auditNumber } });
  }

  /**
   * Mark an audit code as used
   */
  async markAsUsed(auditNumber: string, deviceSerialNumber: string): Promise<DeviceAuditCode> {
    await this.repository.update(
      { auditNumber },
      {
        deviceSerialNumber,
        usedAt: new Date(),
      },
    );
    return this.repository.findOne({ where: { auditNumber } });
  }

  /**
   * Get audit status for a device
   */
  async getAuditStatus(deviceSerialNumber: string): Promise<AuditStatus> {
    const auditCode = await this.findByDeviceSerialNumber(deviceSerialNumber);

    if (!auditCode) {
      return AuditStatus.NOT_PASS;
    }

    // If the device has a used audit code, it is considered PASS
    if (auditCode.usedAt && auditCode.deviceSerialNumber === deviceSerialNumber) {
      return AuditStatus.PASS;
    }

    // All other cases are NOT_PASS, remove AUDITING status
    return AuditStatus.NOT_PASS;
  }

  /**
   * Find all audit codes including soft deleted ones
   * @param limit Maximum number of records to return (default: 100000)
   */
  async findAllIncludingDeleted(limit: number = 100000): Promise<DeviceAuditCode[]> {
    return this.repository.find({
      // No where clause to include all records, even deleted ones
      order: {
        createdAt: 'DESC',
      },
      take: limit,
    });
  }
}
